<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
	<title>脚本评分 | Elasticsearch: 权威指南 | Elastic</title>
    <!-- Give IE8 a fighting chance -->
    <!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
    <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
	<link rel="stylesheet" type="text/css" href="../static/styles.css" />
</head>
<body>
<div class="main-container">
    <section id="content">
        
        <div class="content-wrapper">
            <section id="guide" lang="zh_cn">
                <div class="container">
                    <div class="row">
                        <div class="col-xs-12 col-sm-8 col-md-8 guide-section">
                            <div style="color:gray; word-break: break-all; font-size:12px;">原文地址: <a href="https://www.elastic.co/guide/cn/elasticsearch/guide/current/script-score.html" rel="nofollow">https://www.elastic.co/guide/cn/elasticsearch/guide/current/script-score.html</a>, 版权归 www.elastic.co 所有<br/>
                            英文版地址: <a href="https://www.elastic.co/guide/en/elasticsearch/guide/current/script-score.html" rel="nofollow">https://www.elastic.co/guide/en/elasticsearch/guide/current/script-score.html</a>
                            </div>
                        <!-- start body -->
                  <div class="page_header">
<b>请注意:</b><br>本书基于 Elasticsearch 2.x 版本，有些内容可能已经过时。
</div>
<div id="content">
<div class="breadcrumbs">
<span class="breadcrumb-link"><a href="index.html">Elasticsearch: 权威指南</a></span>
»
<span class="breadcrumb-link"><a href="search-in-depth.html">深入搜索</a></span>
»
<span class="breadcrumb-link"><a href="controlling-relevance.html">控制相关度</a></span>
»
<span class="breadcrumb-node">脚本评分</span>
</div>
<div class="navheader">
<span class="prev">
<a href="Understanding-the-price-Clause.html">« 理解 price 价格语句</a>
</span>
<span class="next">
<a href="pluggable-similarites.html">可插拔的相似度算法 »</a>
</span>
</div>
<div class="section">
<div class="titlepage"><div><div>
<h2 class="title">
<a id="script-score"></a>脚本评分<a class="edit_me edit_me_private" rel="nofollow" title="Editing on GitHub is available to Elastic" href="https://github.com/elasticsearch-cn/elasticsearch-definitive-guide/edit/cn/170_Relevance/65_Script_score.asciidoc">edit</a>
</h2>
</div></div></div>
<p>最后，如果所有 <code class="literal">function_score</code> 内置的函数都无法满足应用场景，可以使用 <code class="literal">script_score</code> 函数自行实现逻辑。</p>
<p>举个例子，想将利润空间作为因子加入到相关度评分计算，在业务中，利润空间和以下三点相关：</p>
<div class="ulist itemizedlist">
<ul class="itemizedlist">
<li class="listitem">
<code class="literal">price</code> 度假屋每晚的价格。
</li>
<li class="listitem">
会员用户的级别——某些等级的用户可以在每晚房价高于某个 <code class="literal">threshold</code> 阀值价格的时候享受折扣 <code class="literal">discount</code> 。
</li>
<li class="listitem">
用户享受折扣后，经过议价的每晚房价的利润 <code class="literal">margin</code> 。
</li>
</ul>
</div>
<p>计算每个度假屋利润的算法如下：</p>
<div class="pre_wrapper lang-groovy">
<pre class="programlisting prettyprint lang-groovy">if (price &lt; threshold) {
  profit = price * margin
} else {
  profit = price * (1 - discount) * margin;
}</pre>
</div>
<p>我们很可能不想用绝对利润作为评分，这会弱化其他如地点、受欢迎度和特性等因子的作用，而是将利润用目标利润 <code class="literal">target</code> 的百分比来表示，高于
目标的利润空间会有一个正向评分（大于 <code class="literal">1.0</code> ），低于目标的利润空间会有一个负向分数（小于 <code class="literal">1.0</code> ）：</p>
<div class="pre_wrapper lang-groovy">
<pre class="programlisting prettyprint lang-groovy">if (price &lt; threshold) {
  profit = price * margin
} else {
  profit = price * (1 - discount) * margin
}
return profit / target</pre>
</div>
<p>Elasticsearch 里使用 <a href="http://groovy.codehaus.org/" class="ulink" target="_top">Groovy</a> 作为默认的脚本语言，它与JavaScript很像，上面这个算法用 Groovy 脚本表示如下：</p>
<div class="pre_wrapper lang-groovy">
<pre class="programlisting prettyprint lang-groovy">price  = doc['price'].value <a id="CO120-1"></a><i class="conum" data-value="1"></i>
margin = doc['margin'].value <a id="CO120-2"></a><i class="conum" data-value="1"></i>

if (price &lt; threshold) { <a id="CO120-3"></a><i class="conum" data-value="2"></i>
  return price * margin / target
}
return price * (1 - discount) * margin / target <a id="CO120-4"></a><i class="conum" data-value="2"></i></pre>
</div>
<div class="calloutlist">
<table border="0" summary="Callout list">
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO120-1"><i class="conum" data-value="1"></i></a><a href="#CO120-2"></a></p>
</td>
<td align="left" valign="top">
<p><code class="literal">price</code> 和 <code class="literal">margin</code> 变量可以分别从文档的 <code class="literal">price</code> 和 <code class="literal">margin</code> 字段提取。</p>
</td>
</tr>
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO120-3"><i class="conum" data-value="2"></i></a><a href="#CO120-4"></a></p>
</td>
<td align="left" valign="top">
<p><code class="literal">threshold</code> 、 <code class="literal">discount</code> 和 <code class="literal">target</code> 是作为参数 <code class="literal">params</code> 传入的。</p>
</td>
</tr>
</table>
</div>
<p>最终我们将 <code class="literal">script_score</code> 函数与其他函数一起使用：</p>
<div class="pre_wrapper lang-json">
<pre class="programlisting prettyprint lang-json">GET /_search
{
  "function_score": {
    "functions": [
      { ...location clause... }, <a id="CO121-1"></a><i class="conum" data-value="1"></i>
      { ...price clause... }, <a id="CO121-2"></a><i class="conum" data-value="1"></i>
      {
        "script_score": {
          "params": { <a id="CO121-3"></a><i class="conum" data-value="2"></i>
            "threshold": 80,
            "discount": 0.1,
            "target": 10
          },
          "script": "price  = doc['price'].value; margin = doc['margin'].value;
          if (price &lt; threshold) { return price * margin / target };
          return price * (1 - discount) * margin / target;" <a id="CO121-4"></a><i class="conum" data-value="3"></i>
        }
      }
    ]
  }
}</pre>
</div>
<div class="calloutlist">
<table border="0" summary="Callout list">
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO121-1"><i class="conum" data-value="1"></i></a><a href="#CO121-2"></a></p>
</td>
<td align="left" valign="top">
<p><code class="literal">location</code> 和 <code class="literal">price</code> 语句在 <a class="xref" href="decay-functions.html" title="越近越好">衰减函数</a> 中解释过。</p>
</td>
</tr>
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO121-3"><i class="conum" data-value="2"></i></a></p>
</td>
<td align="left" valign="top">
<p>将这些变量作为参数 <code class="literal">params</code> 传递，我们可以查询时动态改变脚本无须重新编译。</p>
</td>
</tr>
<tr>
<td align="left" valign="top" width="5%">
<p><a href="#CO121-4"><i class="conum" data-value="3"></i></a></p>
</td>
<td align="left" valign="top">
<p>JSON 不能接受内嵌的换行符，脚本中的换行符可以用 <code class="literal">\n</code> 或 <code class="literal">;</code> 符号替代。</p>
</td>
</tr>
</table>
</div>
<p>这个查询根据用户对地点和价格的需求，返回用户最满意的文档，同时也考虑到我们对于盈利的要求。</p>
<div class="tip admon">
<div class="icon"></div>
<div class="admon_content">
<p><code class="literal">script_score</code> 函数提供了巨大的灵活性，可以通过脚本访问文档里的所有字段、当前评分 <code class="literal">_score</code> 甚至词频、逆向文档频率和字段长度规范值这样的信息（参见 see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/5.6/modules-advanced-scripting.html" class="ulink" target="_top">脚本对文本评分</a>）。</p>
<p>有人说使用脚本对性能会有影响，如果确实发现脚本执行较慢，可以有以下三种选择：</p>
<div class="ulist itemizedlist">
<ul class="itemizedlist">
<li class="listitem">
尽可能多的提前计算各种信息并将结果存入每个文档中。
</li>
<li class="listitem">
Groovy 很快，但没 Java 快。可以将脚本用原生的 Java 脚本重新实现。（参见
<a href="https://www.elastic.co/guide/en/elasticsearch/reference/5.6/modules-scripting-native.html" class="ulink" target="_top">原生 Java 脚本</a>）。
</li>
<li class="listitem">
仅对那些最佳评分的文档应用脚本，使用 <a class="xref" href="_Improving_Performance.html#rescore-api" title="结果集重新评分">重新评分</a> 中提到的 <code class="literal">rescore</code> 功能。
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="navfooter">
<span class="prev">
<a href="Understanding-the-price-Clause.html">« 理解 price 价格语句</a>
</span>
<span class="next">
<a href="pluggable-similarites.html">可插拔的相似度算法 »</a>
</span>
</div>
</div>

                  <!-- end body -->
                        </div>
                        <div class="col-xs-12 col-sm-4 col-md-4" id="right_col">
                        
                        </div>
                    </div>
                </div>
            </section>
        </div>
    </section>
</div>
<script src="../static/cn.js"></script>
</body>
</html>